#ifndef __optional_lkjjwfejiojoiwef93323290i98u9hy7yf43jifejji2efjnvnjv__
#define __optional_lkjjwfejiojoiwef93323290i98u9hy7yf43jifejji2efjnvnjv__

#include <type_traits>
#include <exception>

namespace hude {
    namespace opt_exception
	{
#if 1
	    inline void exception()
		{
			std::terminate();
		}
#else
		inline void exception()
		{
			throw std::exception();
		}
#endif
	} //namespace opt_exception

	namespace utils
	{
		struct _optional_null_t {};
		template<typename T>
		class optional_t
		{
			using data_t = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
			
		public:
			optional_t() : has_(false) {}

			optional_t( _optional_null_t ) : has_(false) {}
			
			optional_t(const T& v)
			{
				create(v);
			}

			optional_t(T&& v) : has_(false)
			{
				create(std::move(v));
			}

			~optional_t()
			{
				destroy();
			}

			optional_t(const optional_t& other) : has_(false)
			{
				if (other.is_init())
					assign(other);
			}

			optional_t(optional_t&& other) : has_(false)
			{
				if (other.is_init())
				{
					assign(std::move(other));
					other.destroy();
				}
			}

			optional_t& operator=(optional_t &&other)
			{
				assign(std::move(other));
				return *this;
			}

			optional_t& operator=(const optional_t &other)
			{
				assign(other);
				return *this;
			}

			optional_t& operator=( _optional_null_t )
			{
				this->destroy();
				return *this;
			}

			template<class... Args>
			void emplace(Args&&... args)
			{
				destroy();
				create(std::forward<Args>(args)...);
			}

			bool has_value() const { return is_init(); }

			explicit operator bool() const { return is_init(); }

			T* operator->()
			{
				if (is_init())
				{
					return ((T*) (&dat_));
				} else {
				    opt_exception::exception(); return nullptr;
				}
			}

			T const* operator->() const
			{
				if (is_init())
				{
					return ((T*) (&dat_));
				} else {
				    opt_exception::exception(); return nullptr;
				}
			}

			T& operator*()
			{
				return *((T*) (&dat_));
			}

			T const& operator*() const
			{
				if (is_init())
				{
					return *((T*) (&dat_));
				} else {
				    opt_exception::exception();
				}

				static const T v{};
				return v;
			}

			bool operator == (const optional_t<T>& rhs) const
			{
				return (!bool(*this)) != (!rhs) ? false : (!bool(*this) ? true : (*(*this)) == (*rhs));
			}

			bool operator < (const optional_t<T>& rhs) const
			{
				return !rhs ? false : (!bool(*this) ? true : (*(*this) < (*rhs)));
			}

			bool operator != (const optional_t<T>& rhs)
			{
				return !(*this == (rhs));
			}

			void reset() { this->destroy(); }

		private:
			bool is_init() const { return has_; }

			template<class... Args>
			void create(Args&&... args)
			{
				new (&dat_) T(std::forward<Args> (args)...);
				has_ = true;
			}

			void destroy()
			{
				if (has_)
				{
					has_ = false;
					((T*) (&dat_))->~T();
				}
			}

			void assign(const optional_t& other)
			{
				if (other.is_init())
				{
					copy(other.dat_);
					has_ = true;
				}
				else
				{
					destroy();
				}
			}

			void assign(optional_t&& other)
			{
				if (other.is_init())
				{
					move(std::move(other.dat_));
					has_ = true;
					other.destroy();
				}
				else
				{
					destroy();
				}
			}

			void move(data_t&& val)
			{
				destroy();
				new (&dat_) T(std::move(*((T*) (&val))));
			}

			void copy(const data_t& val)
			{
				destroy();
				new (&dat_) T(*((T*) (&val)));
			}

		private:
			bool has_;
			data_t dat_;
		};
	} //namespace utils

	template< typename _value >
	using optional_t = ::hude::utils::optional_t< _value >;

	using optional_null_t = ::hude::utils::_optional_null_t;

} //namespace hude

#endif //__optional_lkjjwfejiojoiwef93323290i98u9hy7yf43jifejji2efjnvnjv__

